<!DOCTYPE html>
<html lang="zh-cn">
  <head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta name="author" content="Zhou Wei <zromyk@163.com>">
  <title>35个改善编程与设计的有效方法</title>
  <link rel="shortcut icon" href="/favicon.ico">
  <link rel="stylesheet" href="/style/html/pure.css">
  <link rel="stylesheet" href="/style/html/main.css">
  <link rel="stylesheet" href="https://cdn.staticfile.org/font-awesome/4.7.0/css/font-awesome.css">
  <!-- <link rel="stylesheet" href="https://apps.bdimg.com/libs/highlight.js/9.1.0/styles/default.min.css"> -->
<link rel="stylesheet" href="/style/article/highlight/default.min.css">
<link rel="stylesheet" href="/style/article/pell-1.0.6/dist/pell.css">

</head>
<body>
  <div id="menu-background"></div>
  <div id="menu">
    <div class="pure-menu pure-menu-horizontal">
  <ul class="pure-menu-list block-middle">
    <li class="pure-menu-item">
  <a class="pure-menu-heading" href="/index.html">ZROMYK</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link" href="/index.html">主页</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link" href="/public/archive/index.html">归档</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link" href="/public/download/index.html">下载</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link" href="/public/feedback/index.html">反馈</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link" href="/public/about/index.html">关于我</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link" href="https://github.com/zromyk"><i class="fa fa-github" style="font-size:32px"></i></a>
</li>

  </ul>
</div>

  </div>
  <div id="layout">
    <div class="content">
      <div id="nav">
    <div id="navigation" class="navigation">
  <ul class="pure-menu-list">
    <li class="pure-menu-item">
  <a class="pure-menu-link nav2" onclick="animateByNav()" href="#basics-009">基础议题（Basics） 009</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link nav3" onclick="animateByNav()" href="#1-pointers-references-009">条款1：仔细区别 pointers 和 references 009</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link nav4" onclick="animateByNav()" href="#1-null">1. 指针需要进行检查是否为 NULL，而引用不需要</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link nav4" onclick="animateByNav()" href="#2">2. 指针可以不初始化且可以赋值、引用必须被初始化且不能被赋值</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link nav4" onclick="animateByNav()" href="#3">3. 指针可以被重新赋值（指向另一个对象），而引用总是代表它最初获得的那个对象</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link nav4" onclick="animateByNav()" href="#4">4. 在某些操作符中必须使用引用返回一个对象</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link nav4" onclick="animateByNav()" href="#operator">附录：所有 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>operator</code></span> 定义（可能未完整）</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link nav3" onclick="animateByNav()" href="#2-c-012">条款2：最好使用 C++ 转型操作符 012</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link nav4" onclick="animateByNav()" href="#1-static_cast">1. <span style="overflow-x: auto; max-width:100%; display:inline;"><code>static_cast</code></span></a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link nav4" onclick="animateByNav()" href="#2-const_cast">2. <span style="overflow-x: auto; max-width:100%; display:inline;"><code>const_cast</code></span></a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link nav4" onclick="animateByNav()" href="#3-dynamic_cast">3. <span style="overflow-x: auto; max-width:100%; display:inline;"><code>dynamic_cast</code></span></a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link nav4" onclick="animateByNav()" href="#4-reinterpret_cast">4. <span style="overflow-x: auto; max-width:100%; display:inline;"><code>reinterpret_cast</code></span></a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link nav3" onclick="animateByNav()" href="#3polymorphically-016">条款3：绝对不要以多态（polymorphically）方式处理数组 016</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link nav3" onclick="animateByNav()" href="#4-default-constructor-019">条款4：非必要不提供 default constructor 019</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link nav2" onclick="animateByNav()" href="#operators-024">操作符（Operators） 024</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link nav3" onclick="animateByNav()" href="#5-024">条款5：对定制的“类型转换函数”保持警觉 024</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link nav4" onclick="animateByNav()" href="#1">1. 有两种函数允许编译器进行这些的转换</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link nav4" onclick="animateByNav()" href="#2_1">2. 隐式类型转换运算符只是一个样子奇怪的成员函数</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link nav4" onclick="animateByNav()" href="#3-explicit">3. <span style="overflow-x: auto; max-width:100%; display:inline;"><code>explicit</code></span> 关键字</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link nav3" onclick="animateByNav()" href="#6-incrementdecrement">条款6：区别 increment/decrement 操作符的</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link nav3" onclick="animateByNav()" href="#7-035">条款7：千万不要重载＆＆，||和， 操作符 035</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link nav3" onclick="animateByNav()" href="#8-new-delete-038">条款8：了解各种不同意义的 new 和 delete 038</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link nav4" onclick="animateByNav()" href="#1-placement-new">1. <span style="overflow-x: auto; max-width:100%; display:inline;"><code>Placement new</code></span></a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link nav2" onclick="animateByNav()" href="#exceptions-044">异常（Exceptions） 044</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link nav3" onclick="animateByNav()" href="#9-destructors-045">条款9：利用 destructors 避免泄漏资源 045</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link nav3" onclick="animateByNav()" href="#10-constructors-resource-leak-050">条款10：在 constructors 内阻止资源泄漏（resource leak） 050</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link nav3" onclick="animateByNav()" href="#11exceptions-destructors-058">条款11：禁止异常（exceptions）流出 destructors 之外 058</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link nav3" onclick="animateByNav()" href="#12-exception-061">条款12：了解“抛出一个 exception”与“传递一个参数” 或“调用一个虚函数”之间的差异 061</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link nav3" onclick="animateByNav()" href="#13-by-reference-exceptions-068">条款13：以 by reference 方式捕捉 exceptions 068</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link nav3" onclick="animateByNav()" href="#14-exception-specifications-072">条款14：明智运用 exception specifications 072</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link nav3" onclick="animateByNav()" href="#15exception-handling-078">条款15：了解异常处理（exception handling）的成本 078</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link nav2" onclick="animateByNav()" href="#efficiency-081">效率（Efficiency） 081</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link nav3" onclick="animateByNav()" href="#16-80-20-082">条款16：谨记 80-20 法则 082</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link nav3" onclick="animateByNav()" href="#17-lazy-evaluation-085">条款17：考虑使用 lazy evaluation（缓式评估） 085</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link nav3" onclick="animateByNav()" href="#18-093">条款18：分期摊还预期的计算成本 093</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link nav3" onclick="animateByNav()" href="#19-098">条款19：了解临时对象的来源 098</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link nav4" onclick="animateByNav()" href="#1_1">1. 临时对象的来源</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link nav4" onclick="animateByNav()" href="#2-reference-to-non-const-reference-to-const">2. reference-to-non-const 和 reference-to-const</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link nav4" onclick="animateByNav()" href="#3_1">3. 临时对象是开销</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link nav3" onclick="animateByNav()" href="#20rvo-101">条款20：协助完成“返回值优化（RVO）” 101</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link nav3" onclick="animateByNav()" href="#21overloadimplict-type-conversions105">条款21：利用重载技术（overload）避免隐式类型转换（implict type conversions）105</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link nav3" onclick="animateByNav()" href="#22opop-107">条款22：考虑以操作符复合形式（op=）取代其独身形式（op） 107</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link nav4" onclick="animateByNav()" href="#1-operator-operator">1. 用 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>operator+=</code></span> 实现 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>operator+</code></span></a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link nav4" onclick="animateByNav()" href="#2-operator-operator">2. <span style="overflow-x: auto; max-width:100%; display:inline;"><code>operator+=</code></span> 比 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>operator+</code></span> 的效率要高</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link nav3" onclick="animateByNav()" href="#23-110">条款23：考虑使用其他程序库 110</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link nav3" onclick="animateByNav()" href="#24-virtual-functionsmultiple-inheritancevirtual-base-classes-runtime-type-identification-113">条款24：了解 virtual functions、multiple inheritance、virtual base classes、 runtime type identification 的成本 113</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link nav4" onclick="animateByNav()" href="#1_2">1. 虚函数与虚函数指针</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link nav4" onclick="animateByNav()" href="#2-inline">2. 编译器一般总是忽略虚函数的 inline 指令</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link nav4" onclick="animateByNav()" href="#3_2">3. 多重继承菱形图</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link nav4" onclick="animateByNav()" href="#4_1">4. 运行时类型识别的成本</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link nav2" onclick="animateByNav()" href="#techniques-idioms-patterns-123">技术（Techniques， Idioms， Patterns） 123</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link nav3" onclick="animateByNav()" href="#25-constructor-non-member-functions-123">条款25：将 constructor 和 non-member functions 虚化 123</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link nav3" onclick="animateByNav()" href="#26-class-130">条款26：限制某个 class 所能产生的对象数量 130</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link nav3" onclick="animateByNav()" href="#27-heap-145">条款27：要求（或禁止）对象产生于 heap 之中 145</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link nav4" onclick="animateByNav()" href="#1-heap-heap-base-objects">1. 要求对象产生于 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>heap</code></span> 中，即所谓的 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>Heap-Base Objects</code></span></a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link nav5" onclick="animateByNav()" href="#11">1.1 解决阻止继承的问题</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link nav5" onclick="animateByNav()" href="#12">1.2 解决阻止内含的问题</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link nav4" onclick="animateByNav()" href="#2_2">2. 判断一个对象是否在堆中</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link nav3" onclick="animateByNav()" href="#28smart-pointers-159">条款28：Smart Pointers（智能指针） 159</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link nav3" onclick="animateByNav()" href="#29reference-counting-183">条款29：Reference counting（引用计数） 183</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link nav4" onclick="animateByNav()" href="#reference-counting">一个 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>Reference-Counting</code></span>(引用计数) 基类</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link nav3" onclick="animateByNav()" href="#30proxy-classes-213">条款30：Proxy classes（替身类、代理类） 213</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link nav3" onclick="animateByNav()" href="#31-228">条款31：让函数根据一个以上的对象类型来决定如何虚化 228</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link nav2" onclick="animateByNav()" href="#miscellany-252">杂项讨论（Miscellany） 252</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link nav3" onclick="animateByNav()" href="#32-252">条款32：在未来时态下发展程序 252</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link nav3" onclick="animateByNav()" href="#33non-leaf-classes-abstract-classes-258">条款33：将非尾端类（non-leaf classes）设计为 抽象类（abstract classes） 258</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link nav3" onclick="animateByNav()" href="#34-c-c-270">条款34：如何在同一个程序中结合 C++ 和 C 270</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link nav3" onclick="animateByNav()" href="#35-c-277">条款35：让自己习惯于标准 C++ 语言 277</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link nav2" onclick="animateByNav()" href="#285">推荐读物 285</a>
</li>

  </ul>
</div>

</div>
<div id="content-articles">
  <h1 id="35个改善编程与设计的有效方法" class="content-subhead">35个改善编程与设计的有效方法</h1>
  <p>
    <span>2021-04-17</span>
    <span><span class="post-category post-category-cpp">C++</span></span>
    <button id="button-markdownEditor" class="pure-button" onclick="markdownEditor()">启用编辑</button>
    <button id="button-save" class="pure-button" onclick="save()">保存</button>
  </p>
  <div id="content-articles-markdownEditor" style="display: none;">
    <h1>编辑 power by pell</h1>
    <div id="editor" class="pell"></div>
    <div style="margin-top:20px;">
        <h3>Text output:</h3>
        <div id="text-output"></div>
    </div>
    <div style="margin-top:20px;">
        <h3>HTML output:</h3>
        <pre id="html-output"></pre>
    </div>
  </div>
  <div id="content-articles-markdown">
    <blockquote class="content-quote">
<p>本文只作学习笔记之用，请购买正版书籍：https://item.jd.com/13008184.html</p>
<p>转载：https://blog.csdn.net/fengbingchun/article/details/102990753</p>
</blockquote>
<h2 id="basics-009">基础议题（Basics） 009</h2>
<h3 id="1-pointers-references-009">条款1：仔细区别 pointers 和 references 009</h3>
<p>Distinguish between pointers and references． </p>
<h4 id="1-null">1. 指针需要进行检查是否为 NULL，而引用不需要</h4>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">void printDouble(const double* pd)
{
    if (pd) { // 检查是否为NULL
        std::cout&lt;&lt;*pd;
    }
}

void printDouble(const double&amp; rd)
{
    std::cout&lt;&lt;rd; // 不需要测试rd,它肯定指向一个double值
}
</code></span></code></pre>
<h4 id="2">2. 指针可以不初始化且可以赋值、引用必须被初始化且不能被赋值</h4>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">char* pc = 0;           // 设置指针为空值
char&amp; rc = *pc;         // 让指针指向空值，这是非常有害的，结果将是不确定的

//std::string&amp; rs;      // 错误，引用必须被初始化
std::string s(&quot;xyzzy&quot;);
std::string&amp; rs = s;    // 正确，rs指向s
std::string* ps;        // 未初始化的指针，合法但危险
</code></span></code></pre>
<h4 id="3">3. 指针可以被重新赋值（指向另一个对象），而引用总是代表它最初获得的那个对象</h4>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">std::string s1(&quot;Nancy&quot;);
std::string s2(&quot;Clancy&quot;);
std::string&amp; rs = s1;   // rs引用s1
std::string* ps = &amp;s1;  // ps指向s1
rs = s2;                // rs仍旧引用s1,但是s1的值现在是&quot;Clancy&quot;
ps = &amp;s2;               // ps现在指向s2,s1没有改变
</code></span></code></pre>
<h4 id="4">4. 在某些操作符中必须使用引用返回一个对象</h4>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">// int &amp;operator[](int a) { return *(p + a); }
std::vector&lt;int&gt; v(10);
v[5] = 10;  // 这个被赋值的目标对象就是操作符[]返回的值
            // 如果操作符[]，返回一个指针，那么后一个语句就得这样写: *v[5] = 10;
</code></span></code></pre>
<blockquote class="content-quote">
<p>总结：</p>
<p>在以下情况下你应该使用 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>pointers</code></span>，</p>
<ol>
<li>你考虑到存在不指向任何对象的可能 (在这种情况下，你能够设置指针为空)，</li>
<li>你需要能够在不同的时刻指向不同的对象 (在这种情况下，你能改变指针的指向)。</li>
</ol>
<p>当你知道你需要指向某个东西，而绝对不会改变其指向其他东西，或是当你实现一个操作符而其语法需求无法由 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>pointers</code></span> 达成，你就应该选择 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>references</code></span>。其他任何时候，请采用 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>pointers</code></span>。</p>
</blockquote>
<h4 id="operator">附录：所有 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>operator</code></span> 定义（可能未完整）</h4>
<blockquote class="content-quote">
<p>转载自：https://www.cnblogs.com/hujianglang/p/6221041.html</p>
</blockquote>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">// 普通四则运算
A operator +(const A &amp; a) { A temp(a);  temp.num += this-&gt;num;  return temp; }
A operator -(const A &amp; a) { A temp(a);  temp.num -= this-&gt;num;  return temp; }
A operator *(const A &amp; a) { A temp(a);  temp.num *= this-&gt;num;  return temp; }
A operator /(const A &amp; a) { A temp(a);  temp.num /= this-&gt;num;  return temp; }
A operator %(const A &amp; a) { A temp(a);  temp.num %= this-&gt;num;  return temp; }

friend A operator +(const A &amp; lhs, const A &amp; rhs);
friend A operator -(const A &amp; lhs, const A &amp; rhs);
friend A operator *(const A &amp; lhs, const A &amp; rhs);
friend A operator /(const A &amp; lhs, const A &amp; rhs);
friend A operator %(const A &amp; lhs, const A &amp; rhs);
friend A operator *(const A &amp; lhs, const int &amp; rhs);    //标量运算，如果存在
friend A operator *(const int &amp; lhs, const A &amp; rhs);    //标量运算，如果存在

// 关系操作符
bool operator == (const A &amp; a);
bool operator == (const A &amp; a);
bool operator != (const A &amp; a);
bool operator &lt;  (const A &amp; a);
bool operator &lt;= (const A &amp; a);
bool operator &gt;  (const A &amp; a);
bool operator &gt;= (const A &amp; a);

friend bool operator == (const A &amp; lhs, const A &amp; rhs);
friend bool operator != (const A &amp; lhs, const A &amp; rhs);
friend bool operator &lt;  (const A &amp; lhs, const A &amp; rhs);
friend bool operator &lt;= (const A &amp; lhs, const A &amp; rhs);
friend bool operator &gt;  (const A &amp; lhs, const A &amp; rhs);
friend bool operator &gt;= (const A &amp; lhs, const A &amp; rhs);

// 逻辑操作符
friend bool operator || (const A &amp; lhs, const A &amp; rhs);
friend bool operator &amp;&amp; (const A &amp; lhs, const A &amp; rhs);
bool operator!();

// 正负操作符
A operator +();//取正
A operator -();//取负

// 递增递减操作符
A   operator++(int) { A temp(*this); this-&gt;num++; return temp;  } // A++
A &amp; operator++()    {                this-&gt;num++; return *this; } // ++A
A   operator--(int) { A temp(*this); this-&gt;num--; return temp;  } // A--
A &amp; operator--();   {                this-&gt;num--; return *this; } // ++A

// 动态存储管理操作符：全局或者成员函数均可
// 在 c++11 中引入了新关键字 noexcept 用于替代 throw
void * operator new(std::size_t size)                           throw(bad_alloc); // 可能抛出 bad_alloc 异常
void * operator new(std::size_t size, const std::nothrow_t &amp;)   throw(); // 该函数不抛出任何异常
void * operator new(std::size_t size, void *base)               throw();
void * operator new[](std::size_t size)                         throw(bad_alloc);
void operator delete(void *p);
void operator delete[](void *p);

// 赋值操作符
A &amp;operator =  (A &amp;  a);
A &amp;operator =  (A &amp;&amp; a);
A &amp;operator =  (const A &amp; a) { this-&gt;num =  a.num;  return *this; } 
A &amp;operator += (const A &amp; a) { this-&gt;num += a.num;  return *this; }
A &amp;operator -= (const A &amp; a) { this-&gt;num -= a.num;  return *this; }
A &amp;operator *= (const A &amp; a) { this-&gt;num *= a.num;  return *this; }
A &amp;operator /= (const A &amp; a) { this-&gt;num /= a.num;  return *this; }
A &amp;operator %= (const A &amp; a);

A &amp;operator &amp;= (const A &amp; a);
A &amp;operator |= (const A &amp; a);
A &amp;operator ^= (const A &amp; a);
A &amp;operator &lt;&lt;= (int n);
A &amp;operator &gt;&gt;= (int n);

// 下标操作符
T &amp;operator[] (int i);
const T &amp; operator[](int i)const;   

// 函数调用操作符
T operator()(...); // 参数可选

// 类型转换操作符
operator char *()   const;
operator int()      const;
operator double()   const;

// 逗号操作符
T2 operator,(T1 t1, T2 t2); // 不建议重载

// 指针与选员操作符
A * operator &amp; ();              // 取址操作符

A &amp; operator * ();              // 引领操作符
const A &amp; operator *() const;   // 引领操作符

C * operator-&gt;();               // 选员操作符
const C * operator-&gt;() const;   // 选员操作符
C &amp; operator-&gt;*(...);           // 选员操作符，指向类成员的指针

// 流操作符
friend istream &amp; operator &gt;&gt; (istream &amp;is, A &amp;a)            { is &gt;&gt; a.num;  return is; }
friend ostream &amp; operator &lt;&lt; (ostream &amp;os, const A &amp;a)      { os &lt;&lt; a.num;  return os; }
</code></span></code></pre>
<h3 id="2-c-012">条款2：最好使用 C++ 转型操作符 012</h3>
<p>Prefer C++-style casts． </p>
<p>C++ 导入 4 个新的类型转换操作符：</p>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">static_cast&lt;T&gt;
const_cast&lt;T&gt;
dynamic_cast&lt;T&gt;
reinterpret_cast&lt;T&gt;
</code></span></code></pre>
<h4 id="1-static_cast">1. <span style="overflow-x: auto; max-width:100%; display:inline;"><code>static_cast</code></span></h4>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">int first = 1, second = 1;
double result1 = ((double)first) / second;              // C风格
double result2 = static_cast&lt;double&gt;(first) / second;   // C++风格类型转换
</code></span></code></pre>
<h4 id="2-const_cast">2. <span style="overflow-x: auto; max-width:100%; display:inline;"><code>const_cast</code></span></h4>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">void update(SpeWgt* psw) {}

SpeWgt sw;                          // sw是一个非const对象
const SpeWgt&amp; csw = sw;             // csw是sw的一个引用，它是一个const对象
//update(&amp;csw);                     // 错误，不能传递一个const SpecialWidget*变量给一个处理SpecialWidget*类型变量的函数
update(const_cast&lt;SpeWgt*&gt;(&amp;csw));  // 正确，csw的const显示地转换掉(csw和sw两个变量值在update函数中能被更新)
update((SpeWgt*)&amp;csw);              // 同上，但用了一个更难识别的C风格的类型转换
</code></span></code></pre>
<h4 id="3-dynamic_cast">3. <span style="overflow-x: auto; max-width:100%; display:inline;"><code>dynamic_cast</code></span></h4>
<ul>
<li><strong>dynamic_cast 被用于安全地沿着类的继承关系向下进行类型转换</strong>。这就是说，你能用 dynamic_cast 把指向基类的指针或引用转换成指向其派生类或其兄弟类的指针或引用，而且你能知道转换是否成功。</li>
<li>失败的转换将返回空指针(当对指针进行类型转换时)或者抛出异常(当对引用进行类型转换时)。</li>
<li><strong>它不能被用来缺乏虚函数的类型上。</strong></li>
</ul>
<h4 id="4-reinterpret_cast">4. <span style="overflow-x: auto; max-width:100%; display:inline;"><code>reinterpret_cast</code></span></h4>
<ul>
<li>使用 reinterpret_cast 的代码很难移植。此操作符最普通的用途就是在函数指针之间进行转换。</li>
</ul>
<h3 id="3polymorphically-016">条款3：绝对不要以多态（polymorphically）方式处理数组 016</h3>
<p>Never treat arrays polymorphically． </p>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">class BST {
    public:     virtual ~BST() { fprintf(stdout, &quot;BST::~BST\n&quot;); }
    private:    int score;
};

class BalancedBST : public BST {
    public:     virtual ~BalancedBST() { fprintf(stdout, &quot;BalancedBST::~BalancedBST\n&quot;); }
    private:    int length;
                int size;   // 如果增加此一个int成员
                            // 执行main会segmentation fault
                            // 注释掉此变量，运行正常
};

int main()
{
    fprintf(stdout, &quot;BST size: %d\n&quot;, sizeof(BST)); // 16
    fprintf(stdout, &quot;BalancedBST size: %d\n&quot;, sizeof(BalancedBST)); // 24

    BST* p = new BalancedBST[10];
    delete [] p;    // 如果sizeof(BST) != sizeof(BalancedBST)
                    // 则会segmentation fault

    // 原因：BalancedBST 和 BalancedBST[i] 之间的距离为 i * sizeof(BST)
    // 在以 p 指针方式使用数组时，也会产生相似的错误
    return 0;
}
</code></span></code></pre>
<p>实际测试的结果（令人震惊！）：</p>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">/* 并没有执行 ~BalancedBST() ？？？ */
BST size: 16
BalancedBST size: 24
BST::~BST
BST::~BST
BST::~BST
BST::~BST
BST::~BST
BST::~BST
BST::~BST
BST::~BST
BST::~BST
BST::~BST
</code></span></code></pre>
<p>将上述代码中的动态申请数组删除，改为动态申请一个对象：</p>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">int main()
{
    fprintf(stdout, &quot;BST size: %d\n&quot;, sizeof(BST)); // 16
    fprintf(stdout, &quot;BalancedBST size: %d\n&quot;, sizeof(BalancedBST)); // 24

    BST* p = new BalancedBST;
    delete p;

    return 0;
}
</code></span></code></pre>
<p>可以看到正确结果：</p>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">BST size: 16
BalancedBST size: 24
BalancedBST::~BalancedBST
BST::~BST
</code></span></code></pre>
<p><strong>另外补充：继承时，基类中的析构函数应该申明为 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>virtual</code></span> 这样才能正确析构派生类。</strong></p>
<blockquote class="content-quote">
<p>总结：</p>
<p>C++ 允许你通过基类指针和引用来操作派生类数组。不过这根本就不是一个特性，因为这样的代码几乎从不如你所愿地那样运行。数组与多态不能用在一起。</p>
<p>值得注意的是：</p>
<p>如果你不从一个具体类 (concrete classes)(例如BST) 派生出另一个具体类 (例如BalancedBST)，那么你就不太可能犯这种使用多态性数组的错误。</p>
</blockquote>
<h3 id="4-default-constructor-019">条款4：非必要不提供 default constructor 019</h3>
<p>Avoid gratuitous default constructors． </p>
<p>构造函数能初始化对象，而缺省构造函数则可以不利用任何在建立对象时的外部数据就能初始化对象。有时这样的方法是不错的。例如一些行为特性与数字相仿的对象被初始化为空值或不确定的值也是合理的，还有比如链表、哈希表、图等等数据结构也可以被初始化为空容器。</p>
<p>但不是所有的对象都属于上述类型，对于很多对象来说，不利用外部数据进行完全的初始化是不合理的。比如一个没有输入姓名的地址薄对象，就没有任何意义。</p>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">// 这是一个为公司仪器设计的 class
class EquipmentPiece {
public:
    // 缺少默认构造函数，因为没有设备ID的默认构造函数没有任何意义
    EquipmentPiece(int IDNumber) {}
    // ... others
};

int main()
{
    // EquipmentPiece eqp[10];              // 错误，没有正确调用EquipmentPiece构造函数
    // EquipmentPiece* peqp = new eqp[10];  // 错误，与上面的问题一样

    // 创建数组的方法：----------------------------------------------------

    int ID1 = 1;
    int ID2 = 2;
    // 正确，提供了构造函数的参数，但是此方法不能用于 heap 数组（堆上的动态数组）
    EquipmentPiece bestPieces3[] = { 
        EquipmentPiece(ID1), 
        EquipmentPiece(ID2) 
    };

    // 创建 heap 数组的方法：----------------------------------------------
    // 使用“指针数组”，而非“数组对象”

    typedef EquipmentPiece* PEP;        // PEP指针指向一个EquipmentPiece对象
    PEP  peqp[10];                      // 正确，没有调用构造函数
    PEP* ppeqp = new PEP[10];           // 也正确

    // 在指针数组里的每一个指针被重新赋值，以指向一个不同的EquipmentPiece对象
    for (int i = 0; i &lt; 10; ++i)
        ppeqp[i] = new EquipmentPiece(ID1);

    /* 利用指针数组代替一个对象数组这种方法有两个缺点：

        1. &quot;内存泄漏&quot;，你必须删除数组里每个指针所指向的对象。
                如果忘了，就会发生内存泄漏。

        2. &quot;过度使用内存&quot;，增加了内存分配量，
                因为正如你需要空间来容纳EquipmentPiece对象一样，你也需要空间来容纳指针。
    */

    // 避免 2. &quot;过度使用内存&quot; 的方法：----------------------------------------
    //  为数组分配 raw memory 可以避免浪费内存
    //  使用 placement new 方法在内存中构造 EquipmentPiece 对象

    void* rawMemory = operator new[](10*sizeof(EquipmentPiece));

    // 让 bestPiece 指向此内存块，使这块内存被视为一个 EquipmentPiece 数组。
    EquipmentPiece *bestPiece = static_cast&lt;EquipmentPiece*&gt;(rawMemory);

    // 利用 placement new （见条款8）构造这块内存中的 EquipmentPiece 对象
    for (int i = 0; i &lt; 10; ++i) {
        new(&amp;bestPiece[i]) EquipmentPiece(ID1);
    }

    /* 
        placement new 的缺点：
        1. 大部分程序员不怎么熟悉它，维护起来比较困难
        2. 需要在数组内对象声明结束的时候，手动调用 析构函数
        3. 在最后 调用 delete[] 释放 rawMemory

        **** 注意：此方法不像上面那样需要空间来容纳指针 ****
    */

    // 如果使用普通的数组删除方法，程序的运行将是不可预测的
    for (int i = 9; i &gt;= 0; --i)
        bestPiece[i].~EquipmentPiece(); 
    // deallocate the raw memory
    delete[] rawMemory;

    // **** 注意：以下方法没有定义 ***
    // delete[] bestPiece; // bestPiece 并非来自 operator new

    return 0;
}
</code></span></code></pre>
<blockquote class="content-quote">
<p>对于类里没有定义缺省构造函数还会造成它们无法在许多基于模板(template-based)的容器类里使用。因为实例化一个模板时，模板的类型参数应该提供一个缺省构造函数。在多数情况下，通过仔细设计模板可以杜绝对缺省构造函数的需求。</p>
</blockquote>
<h2 id="operators-024">操作符（Operators） 024</h2>
<h3 id="5-024">条款5：对定制的“类型转换函数”保持警觉 024</h3>
<p>Be wary of user-defined conversion functions． </p>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">template&lt;class T&gt;
class Array {
public:
    explicit Array(int size) {}
    T&amp; operator[](int index) { return data[index]; }
    // ...
private:
    T* data;
};

for (int i = 0; i &lt; 10; ++i) {
    // if (a == b[i]) {} 
        /*
            如果构造函数 Array(int size) 没有 explicit 关键字，
            编译器将能通过调用 Array&lt;int&gt; 构造函数能转换 int 类型到 Array&lt;int&gt; 类型，
            这个构造函数只有一个 int 类型的参数，加上 explicit 关键字则可避免隐式转换
        */

    if (a == Array&lt;int&gt;(b[i])) {}               // 正确，显示从int到Array&lt;int&gt;转换(但是代码的逻辑不合理)
    if (a == static_cast&lt;Array&lt;int&gt;&gt;(b[i]))  {} // 同样正确，同样不合理
    if (a == (Array&lt;int&gt;)b[i]) {}               // C风格的转换也正确，但是逻辑依旧不合理
};
</code></span></code></pre>
<h4 id="1">1. 有两种函数允许编译器进行这些的转换</h4>
<ul>
<li>单参数构造函数(single-argument constructors)。</li>
<li>隐式类型转换运算符。</li>
</ul>
<h4 id="2_1">2. 隐式类型转换运算符只是一个样子奇怪的成员函数</h4>
<p><span style="overflow-x: auto; max-width:100%; display:inline;"><code>operator 类型符号()</code></span> 你不用定义函数的返回类型，因为返回类型就是这个函数的名字。如：</p>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">operator double() const {
    return static_cast&lt;double&gt;(成员变量);
}
</code></span></code></pre>
<h4 id="3-explicit">3. <span style="overflow-x: auto; max-width:100%; display:inline;"><code>explicit</code></span> 关键字</h4>
<p><span style="overflow-x: auto; max-width:100%; display:inline;"><code>explicit</code></span> 关键字是为了解决隐式类型转换而特别引入的这个特性。如果构造函数用 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>explicit</code></span> 声明，编译器会拒绝为了隐式类型转换而调用构造函数。<strong>显式类型转换依然合法。</strong></p>
<h3 id="6-incrementdecrement">条款6：区别 increment/decrement 操作符的</h3>
<p>前置（prefix）和后置（postfix）形式 031 <br />
Distinguish between prefix and postfix forms of increment and decrement operators． </p>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">// 递增递减操作符
A   operator++(int) { A temp(*this); this-&gt;num++; return temp;  } // A++
A &amp; operator++()    {                this-&gt;num++; return *this; } // ++A
A   operator--(int) { A temp(*this); this-&gt;num--; return temp;  } // A--
A &amp; operator--();   {                this-&gt;num--; return *this; } // ++A
</code></span></code></pre>
<p>无论是 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>increment</code></span> 或 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>decrement</code></span> 的前缀还是后缀都只有一个参数，为了解决这个语言问题，C++ 规定后缀形式有一个 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>int</code></span> 类型参数，当函数被调用时，编译器传递一个 0 作为 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>int</code></span> 参数的值给该函数。</p>
<p><strong>在 C++ 中下面的代码是不合法的（编译应该报错）</strong></p>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code>error: expression is not assignable
        a++++;
        ~~~^
</code></span></code></pre>
<blockquote class="content-quote">
<p><strong>当处理用户定义的类型时，尽可能地使用前缀 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>increment</code></span>，因为它的效率较高</strong>。</p>
</blockquote>
<h3 id="7-035">条款7：千万不要重载＆＆，||和， 操作符 035</h3>
<p>Never overload ＆＆， ||， or ，． </p>
<p>在 C/C++ 中，对于“真假值表达式”采用所谓“骤死式”评估方式。</p>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">char *p;
if ((p != nullptr) &amp;&amp; (strlen(p) &gt; 10)) { ... }
// 如果 p == nullptr 就不会执行 (strlen(p) &gt; 10)

if ( 式1 || 式2 ) { ... } // 如果 式1 成立，就不会执行 式2
</code></span></code></pre>
<p>然而，如果对逻辑运算符重载，将会彻底改变“游戏规则”，对逻辑运算符的重载会导致函数调用，它将对所有参数进行评估。</p>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">int test_item_7()
{
    // if (expression1 &amp;&amp; expression2)
    // 如果重载了操作符&amp;&amp;，对于编译器来说，等同于下面代码之一
    // if (expression1.operator&amp;&amp;(expression2))  // when operator&amp;&amp; is a member function
    // if (operator&amp;&amp;(expression1, expression2)) // when operator&amp;&amp; is a global function
    return 0;
}
</code></span></code></pre>
<h3 id="8-new-delete-038">条款8：了解各种不同意义的 new 和 delete 038</h3>
<p>Understand the different meanings of new and delete </p>
<p><span style="overflow-x: auto; max-width:100%; display:inline;"><code>new</code></span><strong><span style="overflow-x: auto; max-width:100%; display:inline;"><code>operator</code></span></strong> 和 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>operator new</code></span> 的区别：</p>
<ul>
<li>
<p><span style="overflow-x: auto; max-width:100%; display:inline;"><code>new</code></span><strong><span style="overflow-x: auto; max-width:100%; display:inline;"><code>operator</code></span></strong> 操作符就像 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>sizeof</code></span> 一样是语言内置的，你不能改变它的含义，它的功能总是一样的。它要完成的功能分成两部分。</p>
<ul>
<li>一、分配足够的内存以便容纳所需类型的对象。</li>
<li>二、调用构造函数初始化内存中的对象。</li>
</ul>
</li>
<li>
<p>new 操作符总是做这两件事情，你不能以任何方式改变它的行为。你所能改变的是如何为对象分配内存。</p>
</li>
<li>new 操作符调用一个函数来完成必须的内存分配，你能够重写或重载这个函数来改变它的行为。new 操作符为分配内存所调用函数的名字是 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>operator new</code></span>。</li>
</ul>
<p><span style="overflow-x: auto; max-width:100%; display:inline;"><code>operator new</code></span> 的声明如下：</p>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">void * operator new(size_t size);
</code></span></code></pre>
<p>和 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>malloc</code></span> 一样，<span style="overflow-x: auto; max-width:100%; display:inline;"><code>operator new</code></span> 的唯一任务就是分配内存。他不知道什么是 <strong>构造函数</strong>，<span style="overflow-x: auto; max-width:100%; display:inline;"><code>operator new</code></span> 只负责分配内存。取得 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>operator new</code></span> 返回的内存并将之转换为一个对象，是 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>new</code></span><strong><span style="overflow-x: auto; max-width:100%; display:inline;"><code>operator</code></span></strong> 的责任。</p>
<h4 id="1-placement-new">1. <span style="overflow-x: auto; max-width:100%; display:inline;"><code>Placement new</code></span></h4>
<p>有时候你真的会想直接调用一个 构造函数。</p>
<p><strong><span style="overflow-x: auto; max-width:100%; display:inline;"><code>Placement</code></span></strong><span style="overflow-x: auto; max-width:100%; display:inline;"><code>new</code></span>：特殊的 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>operator new</code></span>。</p>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">#include &lt;new&gt; // 使用 Placement new 需要包含的头文件
void * operator new(size_t , void * localtion) {
    return localtion;
}
// 不为 size_t 赋予名字，可以防止编译器报警告
</code></span></code></pre>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">class Widget {
public:
    Widget(int widgetSize);
    Widget * constructWidgetInBuffer(void * buffer, int widgetSize) {
        retrun new(buffer) Widget(widgetSize);
        // 隐式调用 void * operator new(size_t , void * localtion)
    }
    // ...
};
</code></span></code></pre>
<p>使用 <strong><span style="overflow-x: auto; max-width:100%; display:inline;"><code>Placement</code></span></strong><span style="overflow-x: auto; max-width:100%; display:inline;"><code>new</code></span> 能够在一块已知的内存上，调用构造函数。</p>
<blockquote class="content-quote">
<p>注意：Deletion and Memory Deallocation：为了避免内存泄漏，每个动态内存分配必须与一个等同相反的deallocation对应。</p>
<p>【<span style="overflow-x: auto; max-width:100%; display:inline;"><code>new</code></span><strong><span style="overflow-x: auto; max-width:100%; display:inline;"><code>operator</code></span></strong>】</p>
<p><span style="overflow-x: auto; max-width:100%; display:inline;"><code>new</code></span><strong><span style="overflow-x: auto; max-width:100%; display:inline;"><code>operator</code></span></strong> 对应 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>delete</code></span><strong><span style="overflow-x: auto; max-width:100%; display:inline;"><code>operator</code></span></strong></p>
<p>【<span style="overflow-x: auto; max-width:100%; display:inline;"><code>operator new</code></span>】</p>
<p><span style="overflow-x: auto; max-width:100%; display:inline;"><code>operator new</code></span> 对应 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>operator delete</code></span></p>
<p>【 <strong><span style="overflow-x: auto; max-width:100%; display:inline;"><code>Placement</code></span></strong><span style="overflow-x: auto; max-width:100%; display:inline;"><code>new</code></span> 】</p>
<p><span style="overflow-x: auto; max-width:100%; display:inline;"><code>pa = new(localtion) A(a);</code></span> 对应 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>pa-&gt;~A();</code></span> </p>
<p>千万不要使用 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>delete pa;</code></span> 和 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>operator delete</code></span>，因为<strong><span style="overflow-x: auto; max-width:100%; display:inline;"><code>Placement</code></span></strong><span style="overflow-x: auto; max-width:100%; display:inline;"><code>new</code></span> 的对象并不是由 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>operator new</code></span> 分配的。</p>
</blockquote>
<h2 id="exceptions-044">异常（Exceptions） 044</h2>
<p>如果一个程序以“设定状态变量”的方式或是利用“返回错误码“的方式发出一个异常信号，无法保证此函数的调用者会检查那个变量或检验那个错误码。于是错误可能会一直传递下去，远离错误发生的地点。</p>
<p>但是如果函数以抛出 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>exception</code></span>  的方式发出异常信号，而该 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>xception</code></span> 未被捕捉，程序的执行便会立即终止。</p>
<h3 id="9-destructors-045">条款9：利用 destructors 避免泄漏资源 045</h3>
<p>Use destructors to prevent resource leaks． </p>
<ul>
<li>用一个对象存储需要被自动释放的资源，然后依靠对象的析构函数来释放资源，这种思想不只是可以运用在指针上，还能用在其它资源的分配和释放上。</li>
<li>资源应该被封装在一个对象里，遵循这个规则，你通常就能够避免在存在异常环境里发生资源泄漏，通过智能指针的方式。</li>
<li><strong>C++ 确保删除空指针是安全的，所以析构函数在删除指针前不需要检测这些指针是否指向了某些对象。</strong></li>
</ul>
<h3 id="10-constructors-resource-leak-050">条款10：在 constructors 内阻止资源泄漏（resource leak） 050</h3>
<p>Prevent resource leaks in constructors． </p>
<ul>
<li><strong>C++ 仅仅能删除被完全构造的对象</strong> (fully constructed objects)，只有一个对象的构造函数完全运行完毕，这个对象才被完全地构造。</li>
<li>C++ 拒绝为没有完成构造操作的对象调用析构函数。</li>
<li>在构造函数中可以使用 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>try catch throw</code></span> 捕获所有的异常。</li>
<li>更好的解决方法是通过智能指针的方式。</li>
<li><strong>如果你用对应的 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>std::unique_ptr</code></span> 对象替代指针成员变量，就可以防止构造函数在存在异常时发生资源泄漏，你也不用手工在析构函数中释放资源，并且你还能像以前使用非 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>const</code></span> 指针一样使用 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>const</code></span> 指针，给其赋值。</strong></li>
</ul>
<blockquote class="content-quote">
<p>待续，转自：https://blog.csdn.net/fengbingchun/article/details/102990753</p>
<p>正版书籍：https://item.jd.com/13008184.html</p>
</blockquote>
<h3 id="11exceptions-destructors-058">条款11：禁止异常（exceptions）流出 destructors 之外 058</h3>
<p>Prevent exceptions from leaving destructors． </p>
<p><strong>禁止异常传递到析构函数外</strong>有两个原因：</p>
<ul>
<li>第一能够在异常传递的堆栈辗转开解 (stack-unwinding) 的过程中，防止 terminate 被调用。</li>
<li>第二它能帮助确保析构函数总能完成我们希望它做的所有事情。</li>
</ul>
<h3 id="12-exception-061">条款12：了解“抛出一个 exception”与“传递一个参数” 或“调用一个虚函数”之间的差异 061</h3>
<p>Understand how throwing an exception differs from <br />
passing a parameter or calling a virtual function． </p>
<ul>
<li>
<p>你调用函数时，程序的控制权最终还会返回到函数的调用处，但是当你抛出一个异常时，控制权永远不会回到抛出异常的地方。</p>
</li>
<li>
<p>C++ 规范要求被作为异常抛出的对象必须被复制。即使被抛出的对象不会被释放，也会进行拷贝操作。抛出异常运行速度比参数传递要慢。</p>
</li>
<li>
<p>当异常对象被拷贝时，拷贝操作是由对象的拷贝构造函数完成的。该拷贝构造函数是对象的静态类型 (static type)所对应类的拷贝构造函数，而不是对象的动态类型 (dynamic type) 对应类的拷贝构造函数。</p>
</li>
<li>
<p><strong>catch</strong> 子句匹配顺序总是取决于它们在程序中出现的顺序。因此一个派生类异常可能被处理其基类异常的 catch 子句捕获，即使同时存在有能直接处理该派生类异常的 catch 子句，与相同的 try 块相对应。不要把处理基类异常的catch子句放在处理派生类异常的catch子句的前面。</p>
</li>
</ul>
<h3 id="13-by-reference-exceptions-068">条款13：以 by reference 方式捕捉 exceptions 068</h3>
<p>Catch exceptions by reference． </p>
<p>通过指针捕获异常不符合C++语言本身的规范。</p>
<p>四个标准的异常</p>
<ul>
<li>bad_alloc (当operator new不能分配足够的内存时被抛出)；</li>
<li>bad<em>cast (当dynamic</em>cast针对一个引用 (reference) 操作失败时被抛出)；</li>
<li>bad<em>typeid (当dynamic</em>cast对空指针进行操作时被抛出)；</li>
<li>bad_exception (用于unexpected异常)</li>
</ul>
<p>都不是指向对象的指针，所以你必须通过值或引用来捕获它们。</p>
<h3 id="14-exception-specifications-072">条款14：明智运用 exception specifications 072</h3>
<p>Use exception specifications judiciously． </p>
<h3 id="15exception-handling-078">条款15：了解异常处理（exception handling）的成本 078</h3>
<p>Understand the costs of exception handling． </p>
<p>采用不支持异常的方法编译的程序一般比支持异常的程序运行速度更快所占空间也更小。</p>
<p>为了减少开销，你应该避免使用无用的try块。如果使用try块，代码的尺寸将增加并且运行速度也会减慢。</p>
<h2 id="efficiency-081">效率（Efficiency） 081</h2>
<h3 id="16-80-20-082">条款16：谨记 80-20 法则 082</h3>
<p>Remember the 80-20 rule． </p>
<p>80-20准则说的是：</p>
<ul>
<li>大约20%的代码使用了80%的程序资源；</li>
<li>大约20%的代码耗用了大约80%的运行时间；</li>
<li>大约20%的代码使用了80%的内存；</li>
<li>大约20%的代码执行80%的磁盘访问；</li>
<li>80%的维护投入于大约20%的代码上。</li>
</ul>
<p>基本的观点：软件整体的性能取决于代码组成中的一小部分。</p>
<h3 id="17-lazy-evaluation-085">条款17：考虑使用 lazy evaluation（缓式评估） 085</h3>
<p>Consider using lazy evaluation． </p>
<h3 id="18-093">条款18：分期摊还预期的计算成本 093</h3>
<p>Amortize the cost of expected computations． </p>
<h3 id="19-098">条款19：了解临时对象的来源 098</h3>
<p>Understand the origin of temporary objects．</p>
<h4 id="1_1">1. 临时对象的来源</h4>
<p>在 C++ 中真正的临时对象是看不见的，它们不出现在你的源代码中。建立一个没有命名的非堆 (non-heap) 对象会产生临时对象。这种未命名的对象通常在两种条件下产生：</p>
<ul>
<li>隐式类型转换以求函数调用成功。</li>
<li>函数返回对象时。 </li>
</ul>
<h4 id="2-reference-to-non-const-reference-to-const">2. reference-to-non-const 和 reference-to-const</h4>
<p>C++ 禁止为非常量引用 (reference-to-non-const) 产生临时对象。</p>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">void uppercasify(string &amp; str) {}

char subtleBookPlug[] = &quot;Effective C++&quot;;
uppercasify(subtleBookPlug); // 错误！
/*
error: no matching function for call to 'uppercasify'
        uppercasify(subtleBookPlug); // 错误！
        ^~~~~~~~~~~
note: candidate function not viable: no known conversion from 'char [14]' to 'std::__1::string &amp;' (aka 'basic_string&lt;char, char_traits&lt;char&gt;, allocator&lt;char&gt; &gt; &amp;') for 1st argument
void uppercasify(string &amp; str) {}
     ^
1 error generated.
*/
</code></span></code></pre>
<p>reference-to-const 是可以编译通过的，编译器会产生一个临时对象，并调用 string 的构造函数来初始化这个临时对象</p>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">void uppercasify(const string &amp; str) {}
</code></span></code></pre>
<h4 id="3_1">3. 临时对象是开销</h4>
<p>临时对象是有开销的，所以你应该尽可能地去除它们。</p>
<ul>
<li>任何时候只要见到常量引用 (reference-to-const) 参数，就存在建立临时对象而绑定在参数上的可能性。</li>
<li>任何时候，只要你看到函数返回一个对象，就会产生临时对象（并于稍后销毁）。</li>
</ul>
<h3 id="20rvo-101">条款20：协助完成“返回值优化（RVO）” 101</h3>
<p>Facilitate the return value optimization． </p>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">class Rational {
public:
    Rational(int numerator = 0, int denominator = 1) {}

    int numerator() const { return 1; }
    int denominator() const { return 2; }
};

const Rational operator*(const Rational&amp; lhs, const Rational&amp; rhs)
{
    // 以某种方法返回对象，能让编译器消除临时对象的开销：
    // 这种技巧是返回 constructor argument 而不是直接返回对象
    return Rational(lhs.numerator() * rhs.numerator(), 
                      lhs.denominator() * rhs.denominator());
}

int test_item_20()
{
    Rational a = 10;
    Rational b(1, 2);
    Rational c = a * b; 
    return 0;
}
</code></span></code></pre>
<p>C++ 规则允许编译器优化不出现的临时对象 (temporary objects out of existence)。</p>
<h3 id="21overloadimplict-type-conversions105">条款21：利用重载技术（overload）避免隐式类型转换（implict type conversions）105</h3>
<p>Overload to avoid implicit type conversions． </p>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">class UPInt { // unlimited precision integers class
public:
    UPInt();
    UPInt(int value);
};

const UPInt operator+(const UPInt&amp; lhs, const UPInt&amp; rhs); // add UPInt+UPInt
const UPInt operator+(const UPInt&amp; lhs, int rhs); // add UPInt+int
const UPInt operator+(int lhs, const UPInt&amp; rhs); // add int+UPInt
// const UPInt operator+(int lhs, int rhs); // 错误！！
// C++ 有一条规则是每一个重载的 operator 必须带有一个用户定义类型 (user-defined type) 的参数。

int test_item_21()
{
    UPInt upi1, upi2;
    UPInt upi3 = upi1 + upi2;   // 正确，没有由 upi1 或 upi2 生成临时对象
    upi3 = upi1 + 10;           // 正确，没有由 upi1 或 10   生成临时对象
    upi3 = 10 + upi2;           // 正确，没有由 10   或 upi2 生成临时对象

    /* 
        注意：注释掉上面的 operator+(UPInt&amp;, int) 和 operator+(int, UPInt&amp;) 也正确，
        但是会通过临时对象把 10 转换为 UPInt
    */
    return 0;
}
</code></span></code></pre>
<p>利用重载避免临时对象的方法不只是用在operator函数上。</p>
<p>没有必要实现大量的重载函数，除非你有理由确信程序使用重载函数以后其整体效率会有显著的提高。</p>
<h3 id="22opop-107">条款22：考虑以操作符复合形式（op=）取代其独身形式（op） 107</h3>
<p>Consider using op= instead of stand-alone op． </p>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">class Rational {
public:
    Rational&amp; operator+=(const Rational&amp; rhs);
    Rational&amp; operator-=(const Rational&amp; rhs);
};

// operator+ 根据 operator+= 实现
const Rational operator+(const Rational&amp; lhs, const Rational&amp; rhs)
{
    return Rational(lhs) += rhs;
}

// operator- 根据 operator -= 实现
const Rational operator-(const Rational&amp; lhs, const Rational&amp; rhs)
{
    return Rational(lhs) -= rhs;
}
</code></span></code></pre>
<p>就 C++ 来说，<span style="overflow-x: auto; max-width:100%; display:inline;"><code>operator+</code></span>、<span style="overflow-x: auto; max-width:100%; display:inline;"><code>operator=</code></span> 和 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>operator+=</code></span> 之间没有任何关系，因此如果你想让三个 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>operator</code></span> 同时存在并具有你所期望的关系，就必须自己实现它们。同理，<span style="overflow-x: auto; max-width:100%; display:inline;"><code>operator-, *, /</code></span> 等等也一样。</p>
<h4 id="1-operator-operator">1. 用 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>operator+=</code></span> 实现 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>operator+</code></span></h4>
<p>确保 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>operator</code></span> 的赋值形式 (assignment version) (例如<span style="overflow-x: auto; max-width:100%; display:inline;"><code>operator+=</code></span>) 与一个 operator 的单独形式 (stand-alone) (例如<span style="overflow-x: auto; max-width:100%; display:inline;"><code>operator+</code></span>) 之间存在正常的关系，一种好方法是 <strong>后者 (指<span style="overflow-x: auto; max-width:100%; display:inline;"><code>operator+</code></span>) 根据前者 (指<span style="overflow-x: auto; max-width:100%; display:inline;"><code>operator+=</code></span>) 来实现</strong>。</p>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">template&lt;class T&gt;
const T operator+(const T&amp; lhs, const T&amp; rhs)
{
    return T(lhs) += rhs;
}
</code></span></code></pre>
<h4 id="2-operator-operator">2. <span style="overflow-x: auto; max-width:100%; display:inline;"><code>operator+=</code></span> 比 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>operator+</code></span> 的效率要高</h4>
<p><span style="overflow-x: auto; max-width:100%; display:inline;"><code>operator+</code></span> 需要返回一个新的对象，负担一个临时对象的构造和析构成本。</p>
<p><span style="overflow-x: auto; max-width:100%; display:inline;"><code>operator+=</code></span> 可以直接将结果写入左端的自变量。</p>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">Rational a, b, c, d, result;

result = a + b + c + d; // 可能会用到 3 个临时变量

result = a;     // 无临时变量
result += b;    // 无临时变量
result += c;    // 无临时变量
result += d;    // 无临时变量
</code></span></code></pre>
<h3 id="23-110">条款23：考虑使用其他程序库 110</h3>
<p>Consider alternative libraries． </p>
<p>不同的程序库在效率、可扩展性、移植性、类型安全和其它一些领域上蕴含着不同的设计理念，通过变换使用给予性能更多考虑的程序库，你有时可以大幅度地提供软件的效率。</p>
<h3 id="24-virtual-functionsmultiple-inheritancevirtual-base-classes-runtime-type-identification-113">条款24：了解 virtual functions、multiple inheritance、virtual base classes、 runtime type identification 的成本 113</h3>
<p>Understand the costs of virtual functions， multiple inheritance， <br />
virtual base classes， and RTTI． </p>
<h4 id="1_2">1. 虚函数与虚函数指针</h4>
<ul>
<li>当调用一个虚拟函数时，被执行的代码必须与调用函数的对象的动态类型相一致；指向对象的指针或引用的类型是不重要的。大多数编译器是使用 <strong>virtual table</strong> 和 <strong>virtual table pointers</strong>，通常被分别地称为 vtbl 和 vptr。</li>
<li>一个 vtbl 通常是一个函数指针数组。(一些编译器使用链表来代替数组，但是基本方法是一样的) 在程序中的每个类只要声明了虚函数或继承了虚函数，它就有自己的 vtbl，并且类中 vtbl 的项目是指向虚函数实现体的指针。</li>
<li>你必须为每个包含虚函数的类的 virtual table 留出空间。类的 vtbl 的大小与类中声明的虚函数的数量成正比(包括从基类继承的虚函数)。每个类应该只有一个 virtual table，所以 virtual table 所需的空间不会太大，但是如果你有大量的类或者在每个类中有大量的虚函数，你会发现 vtbl 会占用大量的地址空间。</li>
</ul>
<h4 id="2-inline">2. 编译器一般总是忽略虚函数的 inline 指令</h4>
<ul>
<li>“inline” 意味着在编译时期，将调用端的调用动作被调用函数本体取代。</li>
<li>“virtual” 意味着等待，直到运行时期才知道那个函数被调用。</li>
</ul>
<h4 id="3_2">3. 多重继承菱形图</h4>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">class A { ... };
class B : virtual public A { ... };
class C : virtual public A { ... };
class D: public B, public C { ... };
</code></span></code></pre>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">class A
{
public:
    int A1_1;
    int A1_2;
    virtual void A1_fun1() {}
    virtual void A1_fun2() {}
};

class B : virtual public A
{
public:
    int B2_1;
    int B2_2;
    virtual void B2_fun1() {}
    virtual void B2_fun2() {}
};

class C : virtual public A
{
public:
    int C3_1;
    int C3_2;
    virtual void C3_fun1() {}
    virtual void C3_fun2() {}
};

class D : public B, public C
{
public:
    int D1_1;
    int D1_2;
    virtual void D1_fun1() {}
    virtual void D1_fun2() {}
};

/*
    vftable : 虚函数表
    vfptr   : 虚函数指针
    vbptr   : 虚基指针              
*/
                                   +-----------&gt; D::$vftable@B@:
                                   |                    | &amp;D_meta
                                   |                    |  0
                                   |             0      | &amp;B::B2_fun1
class D size(52):                  |             1      | &amp;B::B2_fun2   
        +---                       |             2      | &amp;D::D1_fun1
 0      | +--- (base class B)      |             3      | &amp;D::D1_fun2
 0      | | {vfptr} ---------------+
 4      | | {vbptr} ---------------------------&gt; D::$vbtable@B@:    
 8      | | B2_1                                 0      | -4            
12      | | B2_2                                 1      | 36 (Dd(B+4)A)
        | +---                              
16      | +--- (base class C)      
16      | | {vfptr} --------------------------&gt; D::$vftable@C@:
20      | | {vbptr} ---------------+                    | -16
24      | | C3_1                   |             0      | &amp;C::C3_fun1
28      | | C3_2                   |             1      | &amp;C::C3_fun2
        | +---                     |
32      | D1_1                     +----------&gt; D::$vbtable@C@:
36      | D1_2                                   0      | -4
        +---                                     1      | 20 (Dd(C+4)A)
        +--- (virtual base A)       
40      | {vfptr} ----------------------------&gt; D::$vftable@A@:
44      | A1_1                                          | -40
48      | A1_2                                   0      | &amp;A::A1_fun1
        +---                                     1      | &amp;A::A1_fun2

D::D1_fun1 this adjustor: 0         
D::D1_fun2 this adjustor: 0 
vbi:       class  offset o.vbptr  o.vbte fVtorDisp
               A      40       4       4 0
</code></span></code></pre>
<p><strong>virtual base class 可能导致对象内的隐藏指针增加。</strong></p>
<h4 id="4_1">4. 运行时类型识别的成本</h4>
<p>RTTI (运行时类型识别) 能让我们在运行时找到对象和类的有关信息，所以肯定有某个地方存储了这些信息让我们查询。这些信息被存储在类型为 type<em>info 的对象里，你能通过使用 typeid 操作符访问一个类的 type</em>info 对象。</p>
<p>RTTI 被设计为在类的 vtbl 基础上实现。</p>
<h2 id="techniques-idioms-patterns-123">技术（Techniques， Idioms， Patterns） 123</h2>
<h3 id="25-constructor-non-member-functions-123">条款25：将 constructor 和 non-member functions 虚化 123</h3>
<p>Virtualizing constructors and non-member functions． </p>
<h3 id="26-class-130">条款26：限制某个 class 所能产生的对象数量 130</h3>
<p>Limiting the number of objects of a class． </p>
<p>阻止建立某个类的对象，最容易的方法就是把该类的构造函数声明在类的 private 域。</p>
<h3 id="27-heap-145">条款27：要求（或禁止）对象产生于 heap 之中 145</h3>
<p>Requiring or prohibiting heap-based objects． </p>
<ul>
<li>某些时候，你需要让某些类有能够“自杀“的能力，就是说它能够 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>delete this</code></span>，此时就需要确保该对象诞生于 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>heap</code></span> 中。</li>
<li>另一些时候，你需要保证某些类绝不会发生内存泄漏，使没有任何一个该类型的对象从 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>heap</code></span> 分配出来。</li>
</ul>
<h4 id="1-heap-heap-base-objects">1. 要求对象产生于 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>heap</code></span> 中，即所谓的 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>Heap-Base Objects</code></span></h4>
<p>阻止 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>clients</code></span> 不得使用 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>new</code></span> 以外的方法产生对象。<span style="overflow-x: auto; max-width:100%; display:inline;"><code>non-heap Obgects</code></span> 会在其定义点自动构造，并在其寿命结束时自动析构，所以只要让那些隐式调用的构造动作和析构动作不合法就可以了。</p>
<p>方法一：将 <strong>析构函数</strong> 定义为 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>private</code></span></p>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">class UPNumber {
public:
    UPNumber();
    UPNumber(int initValue);
    UPNumber(double initValue);
    UPNumber(const UPNumber &amp; rhs);

    void destroy() const { delete this; }
private:
    ~UPNumber(); // 注意：析构函数位于 private
};

// Clients 需要这么写：
// UPNumber n;  // 错误，当 ~UPNumber(); 被隐藏掉后，隐式调用 private 析构函数 将不合法
UPNumber *p = new UPNumber;
// delete P;    // 错误，显式调用 private 析构函数
p-&gt;destroy();   // 正确
</code></span></code></pre>
<p>方法二：将所有构造函数都声明为 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>private</code></span></p>
<p>但是 构造函数往往有许多个，其中还包括拷贝构造函数，默认构造函数等，而析构函数只有一个，因此应该尽量使用方法一。</p>
<p>只要限制 <strong>构造函数</strong> 和 <strong>析构函数</strong> 就可以阻止 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>non-heap Objects</code></span> 的诞生。但是这样就阻止了继承（inheritance）和内含（containment）：</p>
<h5 id="11">1.1 解决阻止继承的问题</h5>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">class UPNumber { ... private: ~UPNumber(); };
// class NonNegativeUPNumber : public UPNumber {...};
// 错误，析构函数为 private ，不能够继承
// 解决方法，将析构函数 声明 为 protected
</code></span></code></pre>
<h5 id="12">1.2 解决阻止内含的问题</h5>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">class UPNumber { ... private: ~UPNumber(); };
class Asset {
private:
    UPNumber value; // private: ~UPNumber(); 无法通过编译
    // ...
};
// 可以定义一个 UPNumber 类型的指针，指向 UPNumber 对象来解决该问题。
</code></span></code></pre>
<h4 id="2_2">2. 判断一个对象是否在堆中</h4>
<p>但是，现在通过继承，<span style="overflow-x: auto; max-width:100%; display:inline;"><code>NonNegativeUPNumber</code></span> 类的 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>UPNumber</code></span> 成分可以不在 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>heap</code></span> 中：</p>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">class UPNumber { ... protected: ~UPNumber(); };
class NonNegativeUPNumber : public UPNumber {...};

NonNegativeUPNumber n; // 没问题，但是 n 的 UPNumber 成分不在 heap 中。
</code></span></code></pre>
<h3 id="28smart-pointers-159">条款28：Smart Pointers（智能指针） 159</h3>
<h3 id="29reference-counting-183">条款29：Reference counting（引用计数） 183</h3>
<blockquote class="content-quote">
<p>引用计数的核心：将一个 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>struct</code></span> 嵌套进一个 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>class</code></span> 的 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>private</code></span> 段落内，可以很方便地让该 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>class</code></span> 的所有 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>members</code></span> 有权处理这个 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>struct</code></span>，而又禁止其他任何人访问这个 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>struct</code></span>（<span style="overflow-x: auto; max-width:100%; display:inline;"><code>class</code></span> 的 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>friend</code></span> 除外）。</p>
</blockquote>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">class String {
public:
    String(const char* initValue = &quot;&quot;);
    String(const String &amp; rhs);
    ~String();
    String &amp; operator=(const String &amp; rhs);
    const char &amp; operator[](int index) const; // for const String
    char &amp; operator[](int index); // for non-const String
private:
    struct StringValue {
        int refCount;   // 引用次数
        char *data;     // 字符串值
        bool shareable; // 标志，以指出它是否为可共享的
        /* shareable 为解决 char *p = str[i]; 的问题而出现，
           一旦调用 char &amp; operator[](int index); 
           shareable 的值将被设置为 false，永远不能被引用计数 */
        StringValue(const char* initValue);
        ~StringValue();
    };  
    StringValue *value; // string 的值；
};
/* String::---------------------------------------------------- */
/* String::StringValue::--------------------------------------- */
String::StringValue::StringValue(const char* initValue)
    : refCount(1)
    : shareable(true)
{
    data = new char[strlen(initValue) + 1];
    strcpy(data, initValue);
}

String::StringValue::~StringValue()
{
    delete[] data;
}
/* String::StringValue::--------------------------------------- */
/* String::---------------------------------------------------- */
String::String(const char* initValue) 
    : value(new StringValue(initValue)) {
    // None
}

String::String(const String&amp; rhs)
{
    if (rhs.value-&gt;shareable) {
        value = rhs.value;
        ++value-&gt;refCount;
    } else {
        value = new StringValue(rhs.value-&gt;data);
    }
}

String::~String()
{
    if (--value-&gt;refCount == 0) {
        delete value;
    }
}

String &amp; String::operator=(const String &amp; rhs)
{
    /* do nothing if the values are already the same */
    if (value == rhs.value) {
        return *this;
    }

    /* destroy *this's value if no one else is using it */
    /* 
        value != rhs.value : （shareable == true 时）
        在 s1 = s2 的过程中，s1 的引用计数 -1
        s2 的引用计数 +1
    */
    if (value-&gt;shareable &amp;&amp; --value-&gt;refCount == 0) {
        delete value;
    }

    /* have *this share rhs's value */
    if (rhs.value-&gt;shareable) {
        value = rhs.value;
        ++value-&gt;refCount;
    } else {
        value = new StringValue(rhs.value-&gt;data);
    }

    return *this;
}

/* 此函数的 const 版实现起来十分直接，因为这只是个读操作，字符串内容不受影响 */
const char &amp; String::operator[](int index) const
{
    // 实际上可以对该对索引进行一些测试 ...
    return value-&gt;data[index];
}

/* 此函数的 non-sconst 版实现起来有完全不同的故事。
   这个函数可能用来读取一个字符，也可能用来写一个字符 */
char &amp; String::operator[](int index)
{
    /* 
        当我们修改 string 时必须小心避免更动其他共享同一个 StringValue 对象的其他 String 对象
        不幸地是，C++编译起无法告诉我们 operator[] 被调用于读取或写操作，
        所以我们必须悲观地假设 non-const operator[] 的所有调用都被用于写 
        （条款 30 介绍的 proxy classes 可以帮助我们区分读写 ）

        if we're sharing a value with other String objects, 
        break off a separate copy of the value fro ourselves
    */
    if (value-&gt;refCount &gt; 1) {
        // 将目前实际值的引用次数 -1，因为我们不再使用该值
        // decrement current value's refCount, becuase we won't be using that value any more

        --value-&gt;refCount; 

        // 为自己做一份新副本
        // make a copy of the value for ourselves

        value = new StringValue(value-&gt;data); 

        /* 
            ”和其他对象共享一份实际值，直到必须对拥有的那一份实际值进行写动作“
            这个观念在计算机科学领域有很长的历史。
            特别是在操作系统领域，各进程 processes 之间往往允许共享某些内存分页 memory pages，
            直到他们打算修改属于自己的那一分页。

            这项技术有个专有名称：copy-on-write（写时才复制）
            这是提升效率的一般做法（lazy evaluation，缓式评估，见【条款 17】）
        */
    }

    value-&gt;shareable = false;
    // 返回一个引用，代表我们这个“绝对未被共享“的 StringValue 对象内的一个字符
    // return a reference to a character inside our unshared StringValue object
    return value-&gt;data[index];
}
</code></span></code></pre>
<h4 id="reference-counting">一个 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>Reference-Counting</code></span>(引用计数) 基类</h4>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">// 产生一个 base class RCObject 作为 “Reference-Counting 对象”
// 任何其他需要引用计数能力的类，都需要继承这个 RCObject 类
class RCObject {
public:
    // 将引用计数器本身以及用增减计数值的函数封装起来
    void addReference() { ++refCount; }
    void removeReference() { if (--refCount == 0) delete this; } // 必须确保RCObject只能被构建在堆
    void markUnshareable() { shareable = false; }
    bool isShareable() const { return shareable; }
    bool isShared() const { return refCount &gt; 1; }

protected:
    RCObject() 
        : refCount(0), shareable(true) {}
    RCObject(const RCObject&amp; rhs) 
        : refCount(0), shareable(true) {}
    // RCObject 的赋值操作符看起来是个危险分子：它什么也没做
    // 实际上 它并不会被调用
    RCObject&amp; operator=(const RCObject&amp; rhs) { return *this; }
    virtual ~RCObject() = 0; // 纯虚函数，表示 RCObject 为一个纯虚类，只能被用来作为基类，不能定义对象

private:
    int refCount;
    bool shareable; // 用来追踪是否可以共享
};

// 虚析构函数必须被定义出来，即使他们是纯虚函数而且什么也不做
// 原因见【条款 33】和【条款 E14】
// virtual dtors must always be implemented, 
// even if they are pure virtual and do nothing
RCObject::~RCObject() {} 
</code></span></code></pre>
<h3 id="30proxy-classes-213">条款30：Proxy classes（替身类、代理类） 213</h3>
<p>实现二维数组：</p>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">template&lt;class T&gt;
class Array2D { // 使用代理实现二维数组
public:
    Array2D(int row, int col) : row(row), col(col) {
        data.reset(new T[row * col]);
    }

    // Array1D是一个代理类，它的实例扮演的是一个在概念上不存在的一维数组
    class Array1D { 
    public:
        Array1D(T* data) : data(data) {}
        T&amp; operator[](int index) { return data[index]; }
        const T&amp; operator[](int index) const { return data[index]; }

    private:
        T* data;
    };

    Array1D operator[](int index) { 
        return Array1D(data.get() + col * index); 
    }

    const Array1D operator[](int index) const { 
        return Array1D(data.get() + col * index); 
    }

private:
    std::unique_ptr&lt;T[]&gt; data;
    int row, col;
};
</code></span></code></pre>
<h3 id="31-228">条款31：让函数根据一个以上的对象类型来决定如何虚化 228</h3>
<p>Making functions virtual with respect to more than one object． </p>
<h2 id="miscellany-252">杂项讨论（Miscellany） 252</h2>
<h3 id="32-252">条款32：在未来时态下发展程序 252</h3>
<p>Program in the future tense． </p>
<h3 id="33non-leaf-classes-abstract-classes-258">条款33：将非尾端类（non-leaf classes）设计为 抽象类（abstract classes） 258</h3>
<p>Make non-leaf classes abstract． </p>
<h3 id="34-c-c-270">条款34：如何在同一个程序中结合 C++ 和 C 270</h3>
<p>Understand how to combine C++ and C in the same program． </p>
<h3 id="35-c-277">条款35：让自己习惯于标准 C++ 语言 277</h3>
<p>Familiarize yourself with the language standard． </p>
<h2 id="285">推荐读物 285</h2>
<p>auto_ptr 实现代码 291 <br />
索引（一）（General Index） 295 <br />
索引（二）（Index of Example Classes，Functions，and Templtes） 313</p>
  </div>
</div>
 
    </div>
  </div>
  <div id="footer-background">
    <div id="footer">
      <div class="legal pure-g">
  <div class="pure-u-1 u-sm-1-2">
    <p class="legal-license"><a href="https://beian.miit.gov.cn/#/Integrated/index">浙ICP备2020038748号</a></p>
  </div>
  <div class="pure-u-1 u-sm-1-2">
    <p class="legal-links"><a href="https://github.com/zromyk">GitHub</a></p>
    <p class="legal-copyright">Copyright © 2021 Wei Zhou. 保留所有权利。</p>
  </div>
</div>
    </div>
  </div>
  <!-- <script src="https://cdn.bootcss.com/jquery/3.2.1/jquery.min.js"></script> -->
  <script src="/style/html/jquery.min.js"></script>
  <script src='/style/article/latex/latest.js?config=TeX-MML-AM_CHTML'></script>
<!-- <script src="https://cdn.geogebra.org/apps/deployggb.js"></script> -->
<script src="/style/article/deployggb.js"></script>
<!-- <script src="https://apps.bdimg.com/libs/highlight.js/9.1.0/highlight.min.js"></script> -->
<script type="text/javascript">
  // 脚本：navigation 随鼠标移动自动变换宽度
  var element = document.getElementById("navigation"); // 获取要操作的元素
  var elementWidth = parseInt(getComputedStyle(element).width);
  var elementLeft = 0;
  var elementRight = 0;
  element.addEventListener('mouseenter', function (event) { // 添加鼠标按下事件的监听器
    elementLeft = element.getBoundingClientRect().left - 10;
    elementRight = element.getBoundingClientRect().left + elementWidth * 3;
    window.addEventListener('mousemove', resize); // 添加全局的鼠标移动事件的监听器
  });

  function resize(event) {
    var minWidth = elementWidth;
    var maxWidth = elementWidth * 2.5;
    // console.log(elementLeft, event.clientX, elementRight, event.clientX - elementLeft + elementWidth / 2);
    if (elementLeft <= event.clientX && event.clientX <= elementRight) {
      var width = event.clientX - elementLeft + elementWidth / 2;
      width = Math.min(width, maxWidth);
      width = Math.max(width, minWidth);
      element.style.width = width + 'px'; // 设置新的宽度样式属性
    }
    else {
      element.style.width = elementWidth + 'px'; // 设置新的宽度样式属性
      stopResize();
    }
  }

  function stopResize() {
    element.style.width = elementWidth + 'px'; // 设置新的宽度样式属性
    // console.log("stopResize", elementLeft, event.clientX, elementRight, event.clientX - elementLeft + elementWidth / 2);
    window.removeEventListener('mousemove', resize); // 移除鼠标移动事件的监听器
  }
</script>
<script src="/style/article/highlight/highlight.min.js"></script>
<script type="text/javascript">
  // 脚本：code语法高亮
  hljs.initHighlightingOnLoad();
</script>
<script>
  function animateByNav() {
    $("html").animate({
        scrollTop: ($(event.target.hash).offset().top - 52)
    }, 300);
  };
</script>
<script src="/style/article/pell-1.0.6/dist/pell.js"></script>
<script>
  // 脚本：自由编辑页面
  var editor = window.pell.init({
    element: document.getElementById('editor'),
    defaultParagraphSeparator: 'p',
    onChange: function(html) {
        document.getElementById('text-output').innerHTML = html
        document.getElementById('html-output').textContent = html
    }
  });

  function markdownEditor() {
    var articles = document.getElementById('content-articles-markdown');
    if (articles.getAttribute("contenteditable") == "true") {
        articles.setAttribute("contenteditable", "false");
        document.getElementById("content-articles-markdownEditor").style.display = "none"; //隐藏
        document.getElementById("button-markdownEditor").innerHTML = "启用编辑";
    } else {
        articles.setAttribute("contenteditable", "true");
        document.getElementById("content-articles-markdownEditor").style.display = ""; //显示
        document.getElementById("button-markdownEditor").innerHTML = "关闭编辑";
    }
  };

  function save() {
      window.alert("保存成功");
  };
</script>

</body>
</html>
